1 using UnityEngine;
2 using
UnityEngine.EventSystems;
3 using
UnityEngine.UI;
4 using
System;
5 using
UnityEngine.Events;
6
7 [DisallowMultipleComponent]
8 [RequireComponent(
typeof(ScrollRect))]
9 public
class ScrollSnap : UIBehaviour, IDragHandler, IEndDragHandler {
10     [SerializeField]
public int startingIndex = 0;
11     [SerializeField]
public bool wrapAround = false;
12     [SerializeField]
public float lerpTimeMilliSeconds = 200f;
13     [SerializeField]
public float triggerPercent = 5f;
14     [Range(
0f, 10f)] public float triggerAcceleration = 1f;
15
16     
public class OnLerpCompleteEvent : UnityEvent {}
17     
public OnLerpCompleteEvent onLerpComplete;
18     
public class OnReleaseEvent : UnityEvent<int> {}
19     
public OnReleaseEvent onRelease;
20
21     
int actualIndex;
22     
int cellIndex;
23     ScrollRect scrollRect;
24     CanvasGroup canvasGroup;
25     RectTransform content;
26     Vector2 cellSize;
27     
bool indexChangeTriggered = false;
28     
bool isLerping = false;
29     DateTime lerpStartedAt;
30     Vector2 releasedPosition;
31     Vector2 targetPosition;
32
33     
protected override void Awake() {
34         
base.Awake();
35         actualIndex = startingIndex;
36         cellIndex = startingIndex;
37         
this.onLerpComplete = new OnLerpCompleteEvent();
38         
this.onRelease = new OnReleaseEvent();
39         
this.scrollRect = GetComponent<ScrollRect>();
40         
this.canvasGroup = GetComponent<CanvasGroup>();
41         
this.content = scrollRect.content;
42         
this.cellSize = content.GetComponent<GridLayoutGroup>().cellSize;
43         content.anchoredPosition =
new Vector2(-cellSize.x * cellIndex, content.anchoredPosition.y);
44         
int count = LayoutElementCount();
45         SetContentSize(count);
46
47         
if(startingIndex < count) {
48             MoveToIndex(startingIndex);
49         }
50     }
51
52     
void LateUpdate() {
53         
if(isLerping) {
54             LerpToElement();
55             
if(ShouldStopLerping()) {
56                 isLerping =
false;
57                 canvasGroup.blocksRaycasts =
true;
58                 onLerpComplete.Invoke();
59                 onLerpComplete.RemoveListener(WrapElementAround);
60             }
61         }
62     }
63     
64     
public void PushLayoutElement(LayoutElement element) {
65         element.transform.SetParent(content.transform,
false);
66         SetContentSize(LayoutElementCount());
67     }
68     
69     
public void PopLayoutElement() {
70         LayoutElement[] elements = content.GetComponentsInChildren<LayoutElement>();
71         Destroy(elements[elements.Length -
1].gameObject);
72         SetContentSize(LayoutElementCount() -
1);
73         
if(cellIndex == CalculateMaxIndex()) {
74             cellIndex -=
1;
75         }
76     }
77     
78     
public void UnshiftLayoutElement(LayoutElement element) {
79         cellIndex +=
1;
80         element.transform.SetParent(content.transform,
false);
81         element.transform.SetAsFirstSibling();
82         SetContentSize(LayoutElementCount());
83         content.anchoredPosition =
new Vector2(content.anchoredPosition.x - cellSize.x, content.anchoredPosition.y);
84     }
85     
86     
public void ShiftLayoutElement() {
87         Destroy(GetComponentInChildren<LayoutElement>().gameObject);
88         SetContentSize(LayoutElementCount() -
1);
89         cellIndex -=
1;
90         content.anchoredPosition =
new Vector2(content.anchoredPosition.x + cellSize.x, content.anchoredPosition.y);
91     }
92     
93     
public int LayoutElementCount() {
94         
int count = 0;
95         
foreach(Transform t in content.transform) {
96             
if(t.GetComponent<LayoutElement>() != null) {
97                 count +=
1;
98             }
99         }
100         
return count;
101     }
102     
103     
public int CurrentIndex {
104         
get {
105             
int count = LayoutElementCount();
106             
int mod = actualIndex % count;
107             
return mod >= 0 ? mod : count + mod;
108         }
109     }
110     
111     
public void OnDrag(PointerEventData data) {
112         
float dx = data.delta.x;
113         
float dt = Time.deltaTime * 1000f;
114         
float acceleration = Mathf.Abs(dx / dt);
115         
if(acceleration > triggerAcceleration && acceleration != Mathf.Infinity) {
116             indexChangeTriggered =
true;
117         }
118     }
119
120     
public void OnEndDrag(PointerEventData data) {
121         
if(IndexShouldChangeFromDrag(data)) {
122             
int direction = (data.pressPosition.x - data.position.x) > 0f ? 1 : -1;
123             SnapToIndex(cellIndex + direction);
124         }
else {
125             StartLerping();
126         }
127     }
128
129     
public void SnapToNext() {
130         SnapToIndex(cellIndex +
1);
131     }
132
133     
public void SnapToPrev() {
134         SnapToIndex(cellIndex -
1);
135     }
136
137     
public void SnapToIndex(int newCellIndex) {
138         
int maxIndex = CalculateMaxIndex();
139         
if(wrapAround && maxIndex > 0) {
140             actualIndex += newCellIndex - cellIndex;
141             cellIndex = newCellIndex;
142             onLerpComplete.AddListener(WrapElementAround);
143         }
else {
144             
// when it's the same it means it tried to go out of bounds
145             
if(newCellIndex >= 0 && newCellIndex <= maxIndex) {
146                 actualIndex += newCellIndex - cellIndex;
147                 cellIndex = newCellIndex;
148             }
149         }
150         onRelease.Invoke(cellIndex);
151         StartLerping();
152     }
153
154     
public void MoveToIndex(int newCellIndex) {
155         
int maxIndex = CalculateMaxIndex();
156         
if(newCellIndex >= 0 && newCellIndex <= maxIndex) {
157             actualIndex += newCellIndex - cellIndex;
158             cellIndex = newCellIndex;
159         }
160         onRelease.Invoke(cellIndex);
161         content.anchoredPosition = CalculateTargetPoisition(cellIndex);
162     }
163
164     
void StartLerping() {
165         releasedPosition = content.anchoredPosition;
166         targetPosition = CalculateTargetPoisition(cellIndex);
167         lerpStartedAt = DateTime.Now;
168         canvasGroup.blocksRaycasts =
false;
169         isLerping =
true;
170     }
171
172     
int CalculateMaxIndex() {
173         
int cellPerFrame = Mathf.FloorToInt(scrollRect.GetComponent<RectTransform>().sizeDelta.x / cellSize.x);
174         
return LayoutElementCount() - cellPerFrame;
175     }
176
177     
bool IndexShouldChangeFromDrag(PointerEventData data) {
178         
// acceleration was above threshold
179         
if(indexChangeTriggered) {
180             indexChangeTriggered =
false;
181             
return true;
182         }
183         
// dragged beyond trigger threshold
184         
var offset = scrollRect.content.anchoredPosition.x + cellIndex * cellSize.x;
185         
var normalizedOffset = Mathf.Abs(offset / cellSize.x);
186         
return normalizedOffset * 100f > triggerPercent;
187     }
188
189     
void LerpToElement() {
190         
float t = (float)((DateTime.Now - lerpStartedAt).TotalMilliseconds / lerpTimeMilliSeconds);
191         
float newX = Mathf.Lerp(releasedPosition.x, targetPosition.x, t);
192         content.anchoredPosition =
new Vector2(newX, content.anchoredPosition.y);
193     }
194
195     
void WrapElementAround() {
196         
if(cellIndex <= 0) {
197             
var elements = content.GetComponentsInChildren<LayoutElement>();
198             elements[elements.Length -
1].transform.SetAsFirstSibling();
199             cellIndex +=
1;
200             content.anchoredPosition =
new Vector2(content.anchoredPosition.x - cellSize.x, content.anchoredPosition.y);
201         }
else if(cellIndex >= CalculateMaxIndex()) {
202             
var element = content.GetComponentInChildren<LayoutElement>();
203             element.transform.SetAsLastSibling();
204             cellIndex -=
1;
205             content.anchoredPosition =
new Vector2(content.anchoredPosition.x + cellSize.x, content.anchoredPosition.y);
206         }
207     }
208     
209     
void SetContentSize(int elementCount) {
210         content.sizeDelta =
new Vector2(cellSize.x * elementCount, content.rect.height);
211     }
212     
213     Vector2 CalculateTargetPoisition(
int index) {
214         
return new Vector2(-cellSize.x * index, content.anchoredPosition.y);
215     }
216
217     
bool ShouldStopLerping() {
218         
return Mathf.Abs(content.anchoredPosition.x - targetPosition.x) < 0.001;
219     }
220 }


when it's the same it means it tried to go out of bounds

acceleration was above threshold

dragged beyond trigger threshold




Trò chơi đua xe động vật trong UNITY Engine 114.847 lượt xem

Gõ tìm kiếm nhanh...